"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Compreendendo o inferno do retorno de chamada: o problema, as soluções e os exemplos de código

Compreendendo o inferno do retorno de chamada: o problema, as soluções e os exemplos de código

Publicado em 2024-11-08
Navegar:484

Understanding Callback Hell: The Problem, Solutions and Code Examples

Callback hell também é um tópico importante em entrevistas técnicas, pois testa a compreensão do desenvolvedor sobre código assíncrono e sua capacidade de refatorar o código para melhor legibilidade e facilidade de manutenção.

Introdução

A programação assíncrona é crucial no desenvolvimento moderno de JavaScript, permitindo a execução sem bloqueio e melhorando o desempenho, especialmente para operações vinculadas a E/S. No entanto, essa conveniência às vezes pode levar a uma condição conhecida como "inferno de retorno de chamada".

Neste artigo, vamos nos aprofundar em:

  1. O que é o inferno de retorno de chamada e como ele surge.
  2. Os problemas que isso cria.
  3. Soluções, incluindo o uso de Promises e async/await.
  4. Exemplos de código para deixar tudo claro.

O que é o Inferno de Retorno de Chamada?

Callback hell, muitas vezes referido como "Pyramid of Doom", ocorre quando várias operações assíncronas aninhadas dependem umas das outras para serem executadas em sequência. Esse cenário leva a uma confusão de retornos de chamada profundamente aninhados, tornando o código difícil de ler, manter e depurar.

Exemplo de inferno de retorno de chamada:

getData(function(data) {
  processData(data, function(processedData) {
    saveData(processedData, function(response) {
      sendNotification(response, function(notificationResult) {
        console.log("All done!");
      });
    });
  });
});

O código acima executa várias operações assíncronas em sequência. Embora funcione, rapidamente se torna incontrolável se mais tarefas forem adicionadas, dificultando a compreensão e a manutenção. A estrutura aninhada se assemelha a uma pirâmide, daí o termo "Pirâmide da Perdição".

O problema do inferno de retorno de chamada

O inferno do retorno de chamada leva a vários problemas:

    Difícil de manter: código profundamente aninhado é difícil de modificar/estender. Você pode introduzir bugs apenas tentando adicionar novas funcionalidades.
  1. Tratamento de erros: o tratamento adequado de erros em diferentes camadas aninhadas torna-se complexo. Você precisa lidar com erros para cada retorno de chamada individual, o que pode levar à repetição de código.
  2. Legibilidade do código: compreender o fluxo de execução torna-se um desafio, especialmente para desenvolvedores não familiarizados com a base de código.
  3. Escalabilidade: À medida que o número de retornos de chamada aninhados aumenta, também aumenta a complexidade, tornando o código não escalável e difícil de depurar.
Promessas: uma solução para o inferno de retorno de chamada

Para mitigar os problemas do inferno de retorno de chamada,

Promessas são usadas em JavaScript. As promessas representam a eventual conclusão (ou falha) de uma operação assíncrona e permitem escrever código limpo e mais gerenciável. Promessas simplificam o código - Com Promessas, a estrutura aninhada é achatada e o tratamento de erros é mais centralizado, tornando o código mais fácil de ler e manter.

Aqui está como seria o exemplo anterior do inferno de retorno de chamada usando Promises:


getData() .then(dados => processData(dados)) .then(processedData => saveData(processedData)) .then(resposta => sendNotification(resposta)) .then(notificationResult => { console.log("Tudo pronto!"); }) .catch(erro => { console.error("Ocorreu um erro:", erro); });
getData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(response => sendNotification(response))
 .then(notificationResult => {
 console.log("All done!");
 })
 .catch(error => {
 console.error("An error occurred:", error);
 });
Essa abordagem elimina retornos de chamada profundamente aninhados. Cada bloco 'então' representa o próximo passo na cadeia, tornando o fluxo muito mais linear e fácil de seguir. O tratamento de erros também é centralizado no bloco 'catch'.

Como funcionam as promessas

As promessas têm três estados possíveis:

  1. Pendente: o estado inicial, significando que a promessa ainda não foi cumprida ou rejeitada.
  2. Cumprido: a operação assíncrona foi concluída com sucesso.
  3. Rejeitado: a operação falhou.
Um objeto Promise fornece métodos '

.then()' e '.catch()' para lidar com cenários de sucesso e falha.

função getData() { retornar nova Promessa((resolver, rejeitar) => { //Simulando uma operação assíncrona (por exemplo, chamada de API) setTimeout(() => { dados const = "Dados de amostra"; resolver(dados); }, 1000); }); } obterDados() .então(dados => { console.log("Dados recebidos:", dados); }) .catch(erro => { console.error("Erro ao buscar dados:", erro); });
getData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(response => sendNotification(response))
 .then(notificationResult => {
 console.log("All done!");
 })
 .catch(error => {
 console.error("An error occurred:", error);
 });
No código acima, a função 'getData()' retorna uma promessa. Se a operação assíncrona for bem-sucedida, a promessa é cumprida com os dados, caso contrário, é rejeitada com erro.

Encadeando Promessas

Uma das principais vantagens do Promises é que eles podem ser encadeados. Isso permite sequenciar operações assíncronas sem aninhamento.


função buscarData() { retornar nova Promessa((resolver, rejeitar) => { setTimeout(() => resolve("Dados obtidos"), 1000); }); } function processData(dados) { retornar nova Promessa((resolver, rejeitar) => { setTimeout(() => resolve(`${dados} e processados`), 1000); }); } function salvarDados(dados) { retornar nova Promessa((resolver, rejeitar) => { setTimeout(() => resolve(`${dados} e salvos`), 1000); }); } buscarDados() .then(dados => processData(dados)) .then(processedData => saveData(processedData)) .então(resultado => { console.log(resultado); // Saída => Dados buscados, processados ​​e salvos }) .catch(erro => console.error("Erro:", erro));
getData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(response => sendNotification(response))
 .then(notificationResult => {
 console.log("All done!");
 })
 .catch(error => {
 console.error("An error occurred:", error);
 });
Ao encadear Promises, o código se torna simples, mais legível e mais fácil de manter.

Async/Await: uma alternativa ainda melhor

Embora as promessas sejam uma melhoria significativa em relação aos retornos de chamada, elas ainda podem se tornar complicadas com cadeias extensas. É aqui que

async/await entra em jogo. A sintaxe
Async/await nos permite escrever código assíncrono de uma forma que se assemelha ao código síncrono. Isso torna seu código mais limpo e fácil de raciocinar.

Usando Async/Await:


função assíncrona performOperations() { tentar { dados const = aguarda getData(); const processadosData = aguarda processData(dados); resposta const = aguarda saveData(processedData); const notificaçãoResult = aguarda sendNotification (resposta); console.log("Tudo pronto!"); } pegar (erro) { console.error("Erro:", erro); } } performOperações();
getData()
 .then(data => processData(data))
 .then(processedData => saveData(processedData))
 .then(response => sendNotification(response))
 .then(notificationResult => {
 console.log("All done!");
 })
 .catch(error => {
 console.error("An error occurred:", error);
 });
No código acima:

    A palavra-chave 'async' é usada para definir uma função assíncrona.
  • 'await' pausa a execução da função até que a promessa seja resolvida ou rejeitada, fazendo com que o código pareça síncrono.
  • O tratamento de erros é muito mais simples, usando um único bloco 'try-catch'.
  • Async/await elimina o inferno de retorno de chamada e longas cadeias de promessas, tornando-o a maneira preferida de lidar com operações assíncronas em JavaScript moderno.

Conclusão

Callback hell é um problema comum em JavaScript que surge ao trabalhar com múltiplas operações assíncronas. Retornos de chamada profundamente aninhados levam a códigos insustentáveis ​​e propensos a erros. No entanto, com a introdução de Promises e async/await, os desenvolvedores agora têm maneiras de escrever códigos mais limpos, gerenciáveis ​​e escaláveis.

Promessas nivelam retornos de chamada aninhados e centralizam o tratamento de erros, enquanto async/await simplifica ainda mais a lógica assíncrona, fazendo-a parecer síncrona. Ambas as técnicas eliminam o caos do inferno de retorno de chamada e garantem que seu código permaneça legível, mesmo à medida que aumenta em complexidade.

Identificadores de mídia social

Se você achou este artigo útil, sinta-se à vontade para se conectar comigo em meus canais de mídia social para obter mais informações:

    GitHub: [AmanjotSingh0908]
  • LinkedIn: [Amanjot Singh Saini]
  • Twitter: [@AmanjotSingh]
Obrigado pela leitura!

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/amanjotsingh/understanding-callback-hell-the-problem-solutions-and-code-examples-3loh?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3